import 'dart:convert';
import 'dart:io';
import 'dart:io' as io;

import 'package:dio/dio.dart';
import 'package:wu_you/data/cache/auth_cache.dart';
import 'package:wu_you/data/cache/language_cache.dart';
import 'package:wu_you/http/http_config.dart';
import 'package:wu_you/model/base_model.dart';
import 'package:wu_you/utils/logger_util.dart';
import 'package:wu_you/utils/login_util.dart';
import 'package:wu_you/utils/string_utils.dart';

class Http {
  static const int CONNECT_TIMEOUT = 15 * 1000;
  static const int RECEIVE_TIMEOUT = 5 * 1000;
  static const int SEND_TIMEOUT = 5 * 1000;

  static Http _instance = Http._internal(useToken: true);
  static Http _instanceWithoutToken = Http._internal(useToken: false);

  factory Http({bool useToken = true}) => useToken ? _instance : _instanceWithoutToken;

  Dio _dio;

  ///通用全局单例，第一次使用时初始化
  Http._internal({bool useToken}){
    if (!useToken) {
      var options = BaseOptions(
        baseUrl: HttpConfig.BASE_URL,
        connectTimeout: CONNECT_TIMEOUT,
        receiveTimeout: RECEIVE_TIMEOUT,
        sendTimeout: SEND_TIMEOUT,
        headers: {
          HttpHeaders.acceptHeader: "application/json",
          HttpHeaders.contentTypeHeader: "application/json;charset=UTF-8",
          "platform": Platform.operatingSystem,
          "version_code": "1.0.0",
        },
      );
      Dio _dio = Dio(options);
      _dio.interceptors.add(LogsInterceptor());
      this._dio = _dio;
      return;
    }
    var options = BaseOptions(
      baseUrl: HttpConfig.BASE_URL,
      connectTimeout: CONNECT_TIMEOUT,
      receiveTimeout: RECEIVE_TIMEOUT,
      sendTimeout: SEND_TIMEOUT,
      headers: {
        HttpHeaders.acceptHeader: "application/json",
        HttpHeaders.contentTypeHeader: "application/json;charset=UTF-8",
        "platform": Platform.operatingSystem,
        "version_code": "1.0.0",
      },
    );
    Dio _dio = Dio(options);
    _dio.interceptors.add(LogsInterceptor());
    this._addToken();
    this._addLanguage();
    this._dio = _dio;
  }

  void _addLanguage() async{
    this.language = await LanguageCache().get();
  }

  void _addToken()async{
    this.token = await AuthTokenCache().get();
  }


  set language(String language) {
    if (this == _instanceWithoutToken) {
      return;
    }
    if (language == null) {
      _dio.options.headers.remove("language");
      return;
    }
    _dio.options.headers["language"] = "$language";
  }


  set token(String token) {
    if (this == _instanceWithoutToken) {
      return;
    }
    if (token == null) {
      _dio.options.headers.remove("token");
      return;
    }
    _dio.options.headers["token"] = "${token}";
  }

  Future<dynamic> get(
      String path, {
        Map<String, dynamic> params,
        Options options,
        ProgressCallback progressCallback,
        CancelToken cancelToken,
        bool showExtraInfo = false,
      }) {
    return _dio.get(path, queryParameters: params, options: options, onReceiveProgress: progressCallback, cancelToken: cancelToken).then((value) {
      return _handleSuccess(value.data,showExtraInfo: showExtraInfo);
    }).catchError((e) {
      if (e is DioError) {
        return _handleError(e);
      } else if (e is Error) {
        LoggerUtil.d(e.stackTrace.toString());
        return Future.error(DioError(type: DioErrorType.response, error: "请求错误，请稍后再试", requestOptions: null));
      } else {
        return Future.error(DioError(type: DioErrorType.response, error: "未知错误类型", requestOptions: null));
      }
    });
  }

  Future<dynamic> post(
      String path, {
        data,
        Map<String, dynamic> queryParameters,
        Options options,
        CancelToken cancelToken,
        ProgressCallback onSendProgress,
        ProgressCallback onReceiveProgress,
        bool showExtraInfo = false,
      }) {
    return _dio.post(path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress).then((value) {
      return _handleSuccess(value.data,showExtraInfo: showExtraInfo);
    }).catchError((e) {
      if (e is DioError) {
        return _handleError(e);
      } else if (e is Error) {
        LoggerUtil.d(e.stackTrace.toString());
        return Future.error(DioError(type: DioErrorType.response, error: "请求错误，请稍后再试", requestOptions: null));
      } else {
        return Future.error(DioError(type: DioErrorType.response, error: "未知错误类型", requestOptions: null));
      }
    });
  }

  Future<dynamic> put(
      String path, {
        data,
        Map<String, dynamic> queryParameters,
        CancelToken cancelToken,
        ProgressCallback onSendProgress,
        ProgressCallback onReceiveProgress,
      }) {
    return _dio.put(path, data: data, queryParameters: queryParameters, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress).then((value) {
      return _handleSuccess(value.data);
    }).catchError((e) {
      if (e is DioError) {
        return _handleError(e);
      } else if (e is Error) {
        LoggerUtil.d(e.stackTrace.toString());
        return Future.error(DioError(type: DioErrorType.response, error: "请求错误，请稍后再试", requestOptions: null));
      } else {
        return Future.error(DioError(type: DioErrorType.response, error: "未知错误类型", requestOptions: null));
      }
    });
  }

  Future<dynamic> delete(
      String path, {
        data,
        Map<String, dynamic> queryParameters,
        CancelToken cancelToken,
      }) {
    return _dio.delete(path, data: data, queryParameters: queryParameters, cancelToken: cancelToken).then((value) {
      return _handleSuccess(value.data);
    }).catchError((e) {
      if (e is DioError) {
        return _handleError(e);
      } else if (e is Error) {
        LoggerUtil.d(e.stackTrace.toString());
        return Future.error(DioError(type: DioErrorType.response, error: "请求错误，请稍后再试", requestOptions: null));
      } else {
        return Future.error(DioError(type: DioErrorType.response, error: "未知错误类型", requestOptions: null));
      }
    });
  }

  Future<dynamic> patch(
      String path, {
        data,
        Map<String, dynamic> queryParameters,
        CancelToken cancelToken,
        ProgressCallback onSendProgress,
        ProgressCallback onReceiveProgress,
      }) {
    return _dio.patch(path, data: data, queryParameters: queryParameters, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress).then((value) {
      return _handleSuccess(value.data);
    }).catchError((e) {
      if (e is DioError) {
        return _handleError(e);
      } else if (e is Error) {
        LoggerUtil.d(e.stackTrace.toString());
        return Future.error(DioError(type: DioErrorType.response, error: "请求错误，请稍后再试", requestOptions: null));
      } else {
        return Future.error(DioError(type: DioErrorType.response, error: "未知错误类型", requestOptions: null));
      }
    });
  }

  Future<Response> download(
      String urlPath,
      savePath, {
        ProgressCallback onReceiveProgress,
        Map<String, dynamic> queryParameters,
        CancelToken cancelToken,
        bool deleteOnError = true,
        String lengthHeader = Headers.contentLengthHeader,
        data,
        Options options,
      }) {
    return _dio.download(
      urlPath,
      savePath,
      onReceiveProgress: onReceiveProgress,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      deleteOnError: deleteOnError,
      lengthHeader: lengthHeader,
      data: data,
      options: options,
    );
  }

  Future<DioError> _handleError(DioError dioError) async {
    if (dioError.response == null) {
      return Future.error(dioError);
    }
    int statusCode = dioError.response.statusCode;
    String message;
    if(dioError.response.data is Map){
      message = dioError.response.data["msg"];
    }else if(dioError.response.data is String){
      try {
        message = StringUtils.toMap(dioError.response.data)["msg"];
      }catch(e){
        message = "未知错误";
      }
    }else{
      message = "未知错误";
    }
    switch (statusCode) {
      case io.HttpStatus.unauthorized:
        dioError.error = message;
        // LoginUtil.jumpLogin();
        return Future.error(dioError);
      case io.HttpStatus.connectionClosedWithoutResponse:
        dioError.error = "系统繁忙，请稍后再试";
        return Future.error(dioError);
      case io.HttpStatus.badRequest:
        dioError.error = message;
        return Future.error(dioError);
      case io.HttpStatus.forbidden:
        dioError.error = message;
        if(message == null){
          dioError.error = "没有权限";
        }
        return Future.error(dioError);
      case io.HttpStatus.notFound:
        if(message != null){
          if(message.length == 0){
            message = "请求错误,请稍后再试";
          }
          dioError.error = message;
        }else{
          dioError.error = "请求错误,请稍后再试";
        }
        return Future.error(dioError);
      case io.HttpStatus.httpVersionNotSupported:
        dioError.error = "请求错误，请稍后再试或更新版本";
        return Future.error(dioError);
      case io.HttpStatus.internalServerError:
        if(message == null){
          dioError.error = "服务器内部错误，请稍后再试";
        }
        dioError.error = message;
        return Future.error(dioError);
      case io.HttpStatus.notImplemented:
      // dioError.error = "服务器内部错误，请稍后再试";
        dioError.error = "系统繁忙，请稍后再试";
        return Future.error(dioError);
      case io.HttpStatus.badGateway:
      case io.HttpStatus.serviceUnavailable:
      case io.HttpStatus.gatewayTimeout:
        dioError.error = "连接服务器失败，请稍后再试";
        return Future.error(dioError);
      default:
        dioError.error = "发生未知错误，请稍后再试";
        return Future.error(dioError);
    }
  }

  Future<dynamic> _handleSuccess(dynamic data,{bool showExtraInfo}) {
    DioError dioError = DioError(
      type: DioErrorType.response,
      requestOptions: null,
    );
    ResponseResult result = _decodeData(data);
    if (result == null) {
      dioError.error = "数据异常，请稍后再试";
      return Future.error(dioError);
    }
    if(result.code == 401 || result.code == 403){
      dioError.error = "暂未登录或token已经过期";
      LoginUtil.jumpLogin();
      return Future.error(dioError);
    }
    if (result.code != 200) {
      dioError.error = result.msg;
      return Future.error(dioError);
    }
    if(showExtraInfo == true){
      return Future.value(result.msg);
    }
    return Future.value(result.data);
  }

  ResponseResult _decodeData(dynamic data) {
    Map<String, dynamic> result;
    if (data is Map) {
      result = data;
    } else if (data is String) {
      result = json.decode(data);
    } else {
      return ResponseResult.failure(msg: "解析数据失败,不支持的返回类型");
    }
    if (result.containsKey(HttpConfig.KEY_CODE)) {
      return ResponseResult.fromJson(result);
    } else {
      return ResponseResult.failure(msg: "解析数据失败");
    }
  }
}

///日志拦截器
class LogsInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    var sb = StringBuffer();
    var request = response.requestOptions;
    sb.writeln("正确响应");
    sb.writeln("URL: ${request.method} ${request.uri}");
    sb.writeln("请求头: ${request.headers}");
    sb.writeln("请求参数: ${request.queryParameters}");
    sb.writeln("请求数据: ${request.data}");
    sb.writeln("状态码: ${response.statusCode}");
    sb.writeln("响应头: ${response.headers}");
    sb.writeln("额外信息: ${response.extra}");
    sb.writeln("响应数据: ${response.data}");
    LoggerUtil.d(sb.toString());
    handler.next(response);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    var request = err.requestOptions;
    var response = err.response;
    var sb = StringBuffer();
    sb.writeln("错误响应");
    sb.writeln("URL: ${request.method} ${request.uri}");
    sb.writeln("请求头: ${request.headers}");
    sb.writeln("请求参数: ${request.queryParameters}");
    sb.writeln("请求数据: ${request.data}");
    if (response == null) {
      sb.writeln("无响应");
      err.error = "网络异常，请检查后再试";
    } else {
      sb.writeln("状态码: ${response.statusCode}");
      sb.writeln("额外信息: ${response.extra}");
      sb.writeln("错误响应数据: ${response.data}");
    }
    LoggerUtil.d(sb.toString());
    handler.next(err);
  }
}
